﻿/*    
Nemulation: A .NET based framework/platform for virtualization applications
Copyright (C) 2011 Bryan Perris

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Globalization;

namespace NEmu.Debugging
{
    /// <summary>
    /// Application log manager.  Provides logging operations and control.  Define "USE_LOGGING" to enable logging.
    /// Use "USE_DEFAULT_LOGGING" for default logging
    /// </summary>
    public static class EmuLog
    {
        static bool s_IgnoreWarnings = false;
        static bool s_IgnoreInfo = false;
        static bool s_IgnoreAsserts = false;
        static List<int> s_Sections = new List<int>();
        /// <summary>
        /// Event fires when log updates
        /// </summary>
        public static event EventHandler<LogEventArgs> LogUpdate;

        #region Logging Methods ...

        /// <summary>
        /// Writes to a log message using a log call reference under a section name.
        /// If the section name is not on the list of sections to show, the message is negated.
        /// </summary>
        /// <param name="sectionId">an object representing the section ID or name</param>
        /// <param name="logMethod">a delegate of a log write method</param>
        /// <param name="message">the message to write</param>
        public static void SectionLog(object sectionId, LogLevel level, string message)
        {
            if (s_Sections.Contains(sectionId.GetHashCode()))
            {
                string _message = sectionId.ToString() + ": " + message;

                switch (level)
                {
                    case LogLevel.Debug: DebugLog(_message); break;
                    case LogLevel.Error: ErrorLog(_message); break;
                    case LogLevel.Info: InfoLog(_message); break;
                    case LogLevel.Notice: InfoLog(_message); break;
                    case LogLevel.Warning: WarningLog(_message); break;
                    default: break;
                }
            }
        }

        /// <summary>
        /// Writes debug info to the log
        /// </summary>
        /// <param name="message">The log message</param>
        [Conditional("DEBUG")]
        public static void DebugLog(string message)
        {
            Write(LogLevel.Debug, "DEBUG: " + message);
        }

        /// <summary>
        /// Writes informtion to log.
        /// </summary>
        /// <param name="message">The log message</param>
        [Conditional("DEBUG")]
        public static void InfoLog(string message)
        {
            if (!s_IgnoreInfo)
                Write(LogLevel.Info, "INFO: " + message);
        }

        /// <summary>
        /// Writes error infomation to log, asserts if USE_ASSERT_ON_ERROR is defined.
        /// </summary>
        /// <param name="message">The log message</param>
        public static void ErrorLog(string message)
        {
            Write(LogLevel.Error, "ERROR: " + message);
            AssertError(message);
        }

        /// <summary>
        /// Writes user friendly messages to the log, used for general info.
        /// </summary>
        /// <param name="message">The log message</param>
        public static void NoticeLog(string message)
        {
            Write(LogLevel.Notice, message);
        }

        /// <summary>
        /// Writes warning messages to log
        /// </summary>
        /// <param name="message">The log message</param>
        public static void WarningLog(string message)
        {
            if (!s_IgnoreWarnings)
                Write(LogLevel.Warning, "WARNING: " + message);
        }

        #endregion

        /// <summary>
        /// Adds a trace listener to the applog
        /// </summary>
        /// <param name="listener">the listener</param>
        /// <returns>the index of the added listener</returns>
        public static int AddTraceListener(TraceListener listener)
        {
            return Debug.Listeners.Add(listener);
        }

        /// <summary>
        /// Removes a listener from the list
        /// </summary>
        /// <param name="index">index value of the listener</param>
        public static void RemoveTraceListener(int index)
        {
            Debug.Listeners.RemoveAt(index);
        }

        /// <summary>
        /// Adds a section ID or name that the log will show
        /// </summary>
        /// <param name="sectionId">the object representing the section ID or name to add</param>
        public static void AddSectionName(object sectionId)
        {
            s_Sections.Add(sectionId.GetHashCode());
        }

        /// <summary>
        /// Removes a section ID or name from the section list the log will show
        /// </summary>
        /// <param name="sectionId">the object representing the section ID or name to remove</param>
        public static void RemoveSectionName(object sectionId)
        {
            s_Sections.Remove(sectionId.GetHashCode());
        }

        /// <summary>
        /// Whether to shows warnigns in log
        /// </summary>
        public static bool IgnoreWarnings
        {
            get { return s_IgnoreWarnings; }
        }

        /// <summary>
        /// Whether to show info in log
        /// </summary>
        public static bool IgnoreInformation
        {
            get { return s_IgnoreInfo; }
        }

        /// <summary>
        /// Whether asserts should be performed
        /// </summary>
        public static bool IgnoreAsserts
        {
            get { return s_IgnoreAsserts; }
            set { s_IgnoreAsserts = value; }
        }

        /// <summary>
        /// The number of listerns currently active
        /// </summary>
        public static int ListenerCount
        {
            get { return Debug.Listeners.Count; }
        }

        static void Write(LogLevel level, string message)
        {
            if (LogUpdate != null)
            {
                LogUpdate(null, new LogEventArgs(level));
            }

            foreach (TraceListener listener in Debug.Listeners)
            {
#if DEBUG
                if (listener == null)
                    continue;
#endif
                listener.WriteLine(message);
            }
        }

        [Conditional("RELEASE")]
        static void AssertError(string message)
        {
            if (!s_IgnoreAsserts)
                Assert(false, message);
        }

        /// <summary>
        /// Asserts an condition, if false, then it popups an error box
        /// </summary>
        /// <param name="condition">assertion condition</param>
        /// <param name="message">The message to show</param>
        public static void Assert(bool condition, string message)
        {
#if DEBUG
            Debug.Assert(condition, message);
#else
            Trace.Assert(condition, message);
#endif
        }

        /// <summary>
        /// Returns a parameterized string
        /// </summary>
        /// <param name="value">the string value</param>
        /// <param name="values">the list of parameter values</param>
        /// <returns></returns>
        public static string ParameterString(string value, params object[] values)
        {
            string newstr = value;

            for (int i = 0; i < values.Length; i++)
            {
                newstr = newstr.Replace("{" + i.ToString(CultureInfo.CurrentCulture) + "}", values[i].ToString());
            }

            return newstr;
        }
    }
}
